/**
 * @file .
 * @author wangxiaowei <wangxiaowei0303@163.com>
 * @date 2015-05-28 01:02
 */

#include <unistd.h>
#include <getopt.h>

#include <stdio.h>
#include <stdlib.h>
#include <random>

#include <opencv2/core/core_c.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

#include "HistUtil.h"

using namespace cv;

void
help(const char *arg0)
{
    printf(
        "%s usage\n"
        "测试 opencv 自带高斯分布随机数生成器,与 libstdc++ 提供的生成器效果\n"
        "%s 接受以下参数:\n"
        "--width,-w;噪声图像的宽度\n"
        "--height,-h;噪声图像的高度\n"
        "--mean,-m;高斯分布的均值\n"
        "--stddev,-s;高斯分布的标准差\n",
        arg0,arg0
    );
    return ;
}

Mat
cvAddNoise(unsigned int width,unsigned int height,double mean,double stddev);

Mat
stdAddNoise(unsigned int width,unsigned int height,double mean,double stddev);

int
main(int argc,char *argv[])
{
    unsigned int w = 0,h = 0;
    double mean = 0,stddev=0;
    int ret;

    struct option options[] = {
        {"width",required_argument,0,'w'},
        {"height",required_argument,0,'h'},
        {"mean",required_argument,0,'m'},
        {"stddev",required_argument,0,'s'},
        {0,0,0,0}
    };
    while ((ret = getopt_long(argc,argv,":w:h:",options,0)) != -1) {
        switch (ret) {
        case 'h':
            h = strtoul(optarg,0,0);
            break;
        case 'w':
            w = strtoul(optarg,0,0);
            break;
        case 'm':
            mean = strtod(optarg,0);
            break;
        case 's':
            stddev = strtod(optarg,0);
            break;
        default:
            break;
        }
    }
    if (w == 0 || h == 0 || 
       (mean == HUGE_VAL || mean == -HUGE_VAL) || 
       (stddev ==HUGE_VAL || stddev == -HUGE_VAL)) 
    {
        fputs("ERROR;参数不合法\n",stderr);
        help(argv[0]);
        return 1;
    }

    printf("INFO;width = %u; height = %u\nINFO;mean = %f; stddev = %f\n",w,h,
        mean,stddev);
    
    Mat noiseImg[2];
    noiseImg[0] = cvAddNoise(w,h,mean,stddev);
    noiseImg[1] = stdAddNoise(w,h,mean,stddev);
    
    unsigned int hist[HistUtil::IMG_GRAY_VAL_MAX + 1];
    for (int i = 0; i < 2; ++i) {
        HistUtil::calc(noiseImg[i],hist);
        Mat histimg = HistUtil::draw(hist);
        std::string imgName(i == 0 ? "cv" : "std");
        imshow(imgName,noiseImg[i]);
        imshow(imgName + "-直方图",histimg);
    }
    waitKey();
    return 0;
}

Mat
cvAddNoise(unsigned int width,unsigned int height,double mean,double stddev)
{
    CvSize imgSize = cvSize(width,height);
    IplImage *noise = cvCreateImage(imgSize,IPL_DEPTH_8U,1);
    CvRNG rng = cvRNG(-1);
    cvRandArr(&rng,noise,CV_RAND_NORMAL,cvScalarAll(mean),cvScalarAll(stddev));
    Mat noiseMat(noise,true);
    cvReleaseImage(&noise);
    return noiseMat;
}

Mat
stdAddNoise(unsigned int width,unsigned int height,double mean,double stddev)
{
    std::mt19937_64 engine;
    engine.seed(time(0));
    std::normal_distribution<double> n(mean,stddev);

    Mat noise(height,width,CV_64FC1);
    MatIterator_<double> iter = noise.begin<double>();
    const MatIterator_<double> end = noise.end<double>();
    for (; iter != end; ++iter)
        *iter = n(engine);

    double minrnd = *std::min_element(noise.begin<double>(),noise.end<double>());
    double maxrnd = *std::max_element(noise.begin<double>(),noise.end<double>());
    
    Mat noise8u(height,width,CV_8UC1);
    MatIterator_<uchar> iter8u = noise8u.begin<uchar>();
    for (iter = noise.begin<double>(); iter != end; ++iter,++iter8u) {
        *iter -= minrnd;
        *iter /= maxrnd - minrnd;
        *iter *= HistUtil::IMG_GRAY_VAL_MAX;
        *iter8u = (uchar)round(*iter);
    }
    return noise8u;
}

